package com.luoye.audiodemo.weight

import android.content.Context
import android.graphics.BitmapFactory
import android.opengl.EGL14
import android.opengl.GLES20
import android.opengl.GLUtils
import android.util.AttributeSet
import android.view.Surface
import android.view.SurfaceHolder
import android.view.SurfaceView
import com.blankj.utilcode.util.LogUtils
import com.luoye.audiodemo.R
import com.luoye.audiodemo.egl.YShaderUtil
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import javax.microedition.khronos.egl.EGL10
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.egl.EGLContext
import javax.microedition.khronos.egl.EGLDisplay
import javax.microedition.khronos.egl.EGLSurface

/**
 * 绘制一张图片
 */
class MyGLSurfaceView4 @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAtt: Int = -1
) : SurfaceView(context, attrs, defStyleAtt), SurfaceHolder.Callback {
    init {
        holder.addCallback(this)
    }

    private val liveHandler = CoroutineExceptionHandler { _, e ->
        // 处理异常
        LogUtils.e(e)
    }
    private val scope = UploadCoroutineScope(SupervisorJob() + Dispatchers.IO + liveHandler)
    override fun surfaceCreated(holder: SurfaceHolder) {
        LogUtils.e("创建")
    }


    override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) {
        LogUtils.e("改变")
        initEgl(holder.surface)
        initOpenGl()
        GLES20.glViewport(0, 0, width, height)
        // 协程
        scope.launch {
            while (true) {
                withContext(Dispatchers.Main) {
                    //清除屏幕，此处用的是红色
                    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
                    GLES20.glClearColor(1f, 0f, 0f, 1f)
                    //使用着色器源程序
                    GLES20.glUseProgram(program!!)
                    //使能顶点属性数组，使之有效
                    GLES20.glEnableVertexAttribArray(vPosition!!)
                    //使能之后，为顶点属性赋值，绑定顶点坐标
                    GLES20.glVertexAttribPointer(vPosition!!, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer)
                    //使能片元属性数组，使之有效
                    GLES20.glEnableVertexAttribArray(fPosition!!)
                    //使能之后，为片元属性赋值，绑定纹理坐标
                    GLES20.glVertexAttribPointer(fPosition!!, 2, GLES20.GL_FLOAT, false, 8, fragmentBuffer)
                    //要开始绘制纹理了，激活纹理 0号， 之所以激活 0号，是因为在没设置点的情况下默认是 0号
                    GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
                    //绑定 textureIds[0] 到已激活的 2D纹理 GL_TEXTURE0上
                    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0])
                    //获取图片的 bitmap
                    val bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.nobb)
                    //绑定 bitmap 到textureIds[0]纹理
                    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0)
                    bitmap.recycle() //用完及时回收
                    //绘制图形
                    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
                    //解绑 2D纹理
                    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)
                    mEgl?.apply {
                        // 刷新。
                        val swap = eglSwapBuffers(mEglDisplay, mEglSurface)
                    }
                }
                // 20 到200 都行。
                delay(20)
            }
        }
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        LogUtils.e("销毁")
    }

    private var mEgl: EGL10? = null //EGL实例

    private var mEglDisplay: EGLDisplay? = null //默认的显示设备

    private var mEglContext: EGLContext? = null //EGL上下文

    private var mEglSurface: EGLSurface? = null //EGLSurface


    var program: Int? = null
    var vPosition: Int? = null
    var fPosition: Int? = null


    //加载顶点着色器 shader
    val vertexSource = "attribute vec4 vPosition;\n" +
            "attribute vec2 fPosition;\n" +
            "varying vec2 ft_Position;\n" +
            "\n" +
            "void main(){\n" +
            "    gl_Position = vPosition;\n" +
            "    ft_Position = fPosition;\n" +
            "}"
    //加载片元着色器 shader
    val fragmentSource ="precision mediump float;\n" +
            "varying vec2 ft_Position;\n" +
            "uniform sampler2D vTexture;\n" +
            "\n" +
            "void main(){\n" +
            "    gl_FragColor = texture2D(vTexture, ft_Position);\n" +
            "}"

    fun initOpenGl() {


        val vertexShader: Int = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource)
        val fragmentShader: Int = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource)
        // glCreateProgram函数创建一个着色器程序，并返回新创建程序对象的ID引用
        program = GLES20.glCreateProgram().also {
            // 把顶点着色器添加到程序对象
            GLES20.glAttachShader(it, vertexShader)
            // 把片段着色器添加到程序对象
            GLES20.glAttachShader(it, fragmentShader)
            // 连接并创建一个可执行的OpenGL ES程序对象
            GLES20.glLinkProgram(it)
        }
        //从渲染程序中得到着顶点色器中的属性
        vPosition = GLES20.glGetAttribLocation(program!!, "vPosition")
        //从渲染程序中得到片元着色器中的属性
        fPosition = GLES20.glGetAttribLocation(program!!, "fPosition")
        //创建 1个纹理,放入到 int [] textureIds, 一共有 30多个 纹理
        GLES20.glGenTextures(1, textureIds, 0) //第三个参数是指从哪儿开始取
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds.get(0))
        //设置纹理的环绕方式
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT)
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT)
        //设置纹理的过滤方式
        GLES20.glTexParameteri(
            GLES20.GL_TEXTURE_2D,
            GLES20.GL_TEXTURE_MIN_FILTER,
            GLES20.GL_LINEAR
        )
        GLES20.glTexParameteri(
            GLES20.GL_TEXTURE_2D,
            GLES20.GL_TEXTURE_MAG_FILTER,
            GLES20.GL_LINEAR
        )
        //解绑纹理 指的是离开对 纹理的配置，进入下一个状态
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)
    }
    private fun loadShader(type: Int, shaderCode: String): Int {
        // glCreateShader函数创建一个顶点着色器或者片段着色器,并返回新创建着色器的ID引用
        val shader = GLES20.glCreateShader(type)
        // 把着色器和代码关联,然后编译着色器
        GLES20.glShaderSource(shader, shaderCode)
        GLES20.glCompileShader(shader)
        return shader
    }
    // 顶点坐标。
    val vertexBuffer: FloatBuffer by lazy {
        //顶点坐标
        val vertexData = floatArrayOf(
            -1f, -1f,
            1f, -1f,
            -1f, 1f,
            1f, 1f

        )
        val buffer = ByteBuffer.allocateDirect(vertexData.size * 4).order(ByteOrder.nativeOrder())
            .asFloatBuffer().put(vertexData)
        buffer.position(0)
        buffer
    }

    // 纹理的buffer
    val fragmentBuffer: FloatBuffer by lazy {
        //顶点坐标
        val vertexData = floatArrayOf(
            0f, 1f,
            1f, 1f,
            0f, 0f,
            1f, 0f
        )
        val buffer = ByteBuffer.allocateDirect(vertexData.size * 4).order(ByteOrder.nativeOrder())
            .asFloatBuffer().put(vertexData)
        buffer.position(0)
        buffer
    }
    var textureIds = IntArray(1)

    fun initEgl(surface: Surface) {

        //1.获取 Egl实例
        mEgl = EGLContext.getEGL() as EGL10

        //2.获取一个默认的显示设备
        mEglDisplay = mEgl!!.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
        if (mEglDisplay === EGL10.EGL_NO_DISPLAY) {
            throw RuntimeException("get EGL_DEFAULT_DISPLAY error")
        }

        //3.初始化默认显示设备
        val version = IntArray(2)
        if (!mEgl!!.eglInitialize(mEglDisplay, version)) {
            throw RuntimeException("init EGL_DEFAULT_DISPLAY error")
        }
        //4.设置显示设备的属性
        val attrs = intArrayOf(
            EGL10.EGL_RED_SIZE, 8,
            EGL10.EGL_GREEN_SIZE, 8,
            EGL10.EGL_BLUE_SIZE, 8,
            EGL10.EGL_ALPHA_SIZE, 8,
            EGL10.EGL_DEPTH_SIZE, 8,
            EGL10.EGL_STENCIL_SIZE, 8,
            EGL10.EGL_RENDERABLE_TYPE, 4,
            EGL10.EGL_NONE
        )
        val num_config = IntArray(1)
        require(
            mEgl!!.eglChooseConfig(
                mEglDisplay,
                attrs,
                null,
                1,
                num_config
            )
        ) { "set eglChooseConfig failed" }
        val numConfigs = num_config[0]
        require(numConfigs > 0) { "No configs match configSpec" }

        //5.从系统中获取对应属性的配置
        val configs = arrayOfNulls<EGLConfig>(numConfigs)
        require(
            mEgl!!.eglChooseConfig(
                mEglDisplay,
                attrs,
                configs,
                numConfigs,
                num_config
            )
        ) { "eglChooseConfig#2 failed" }

        //6.创建 EglContext
        val attr_list = intArrayOf(
            EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL10.EGL_NONE
        )
        mEglContext = mEgl!!.eglCreateContext(
            mEglDisplay,
            configs[0], EGL10.EGL_NO_CONTEXT, attr_list
        )
        //7.创建渲染的 Surface,最后一个参数传空
        mEglSurface = mEgl!!.eglCreateWindowSurface(mEglDisplay, configs[0], surface, null)

        //8、绑定 EglContext和 Surface到显示设备中
        if (!mEgl!!.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
            throw RuntimeException("eglMakeCurrent failed")
        }
    }
}


